# vim: syntax=ruby


class PuppetDB::Parser

  token LPAREN RPAREN LBRACK RBRACK LBRACE RBRACE
  token EQUALS NOTEQUALS MATCH NOTMATCH
  token LESSTHAN LESSTHANEQ GREATERTHAN GREATERTHANEQ
  token AT HASH ASTERISK DOT
  token NOT AND OR
  token NUMBER STRING BOOLEAN EXPORTED

  prechigh
    right NOT
    left  EQUALS MATCH LESSTHAN GREATERTHAN
    left  AND
    left  OR
  preclow

  options no_result_var

rule
  query
    : expression
    |

  expression
    : identifier_path             { ASTNode.new :regexp_node_match, val[0] }
    |            NOT expression   { ASTNode.new :booleanop, :not, [val[1]] }
    | expression AND expression   { ASTNode.new :booleanop, :and, [val[0], val[2]] }
    | expression OR  expression   { ASTNode.new :booleanop, :or, [val[0], val[2]] }
    | LPAREN expression RPAREN    { val[1] }
    | resource_expression
    | comparison_expression
    | subquery

  literal
    : boolean                     { ASTNode.new :boolean, val[0] }
    | string                      { ASTNode.new :string, val[0] }
    | integer                     { ASTNode.new :number, val[0] }
    | float                       { ASTNode.new :number, val[0] }
    | AT string                   { ASTNode.new :date, val[1] }

  comparison_op
    : MATCH
    | NOTMATCH
    | EQUALS
    | NOTEQUALS
    | GREATERTHAN
    | GREATERTHANEQ
    | LESSTHAN
    | LESSTHANEQ

  comparison_expression
    : identifier_path comparison_op literal { ASTNode.new :comparison, val[1], [val[0], val[2]] }

  identifier
    : string                      { ASTNode.new :identifier, val[0] }
    | integer                     { ASTNode.new :identifier, val[0] }
    | MATCH string                { ASTNode.new :regexp_identifier, val[1] }
    | ASTERISK                    { ASTNode.new :regexp_identifier, '.*' }

  identifier_path
    : identifier                     { ASTNode.new :identifier_path, nil, [val[0]] }
    | identifier_path DOT identifier { val[0].children.push val[2]; val[0] }

  subquery
    : HASH string DOT comparison_expression { ASTNode.new :subquery, val[1], [val[3]] }
    | HASH string block_expression          { ASTNode.new :subquery, val[1], [val[2]] }

  block_expression
    : LBRACE expression RBRACE    { val[1] }

  resource_expression
    : string LBRACK identifier RBRACK
      { ASTNode.new :resource, {:type => val[0], :title => val[2], :exported => false} }
    | string LBRACK identifier RBRACK block_expression
      { ASTNode.new :resource, {:type => val[0], :title => val[2], :exported => false}, [val[4]] }
    | EXPORTED string LBRACK identifier RBRACK
      { ASTNode.new :resource, {:type => val[1], :title => val[3], :exported => true} }
    | EXPORTED string LBRACK identifier RBRACK block_expression
      { ASTNode.new :resource, {:type => val[1], :title => val[3], :exported => true}, [val[5]] }

  boolean: BOOLEAN
  integer: NUMBER
  string: STRING
  float: NUMBER DOT NUMBER        { "#{val[0]}.#{val[2]}".to_f }

end
---- inner
  include PuppetDB::ParserHelper

---- header
require 'puppetdb'
require 'puppetdb/lexer'
require 'puppetdb/astnode'
require 'puppetdb/parser_helper'
